package com.ncu.xrpc.server;

import cn.hutool.core.util.StrUtil;
import com.ncu.xrpc.ServiceRegistry;
import com.ncu.xrpc.anno.XRpcService;
import com.ncu.xrpc.handler.RpcServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: xiongwb
 * Date: 2021/9/11 19:41
 * Describe: Rpc服务端（后置处理器）
 */
@Slf4j
public class RpcServer implements ApplicationContextAware, InitializingBean {

    /**
     * 提供服务的服务地址
     */
    private String serviceAddress;

    /**
     * 进行服务注册
     */
    private ServiceRegistry serviceRegistry;

    /**
     * 服务名  ---> 服务对象
     */
    private Map<String, Object> handlerMap = new HashMap<>();

    /**
     * 生命一个RpcSrvere必须要有服务地址
     * @param serviceAddress
     */
    public RpcServer(String serviceAddress){this.serviceAddress = serviceAddress;}

    /**
     * @param serviceAddress
     * @param serviceRegistry
     */
    public RpcServer(String serviceAddress, ServiceRegistry serviceRegistry){
        this.serviceAddress = serviceAddress;
        this.serviceRegistry = serviceRegistry;
    }

    public NioEventLoopGroup boss;
    public NioEventLoopGroup worker;

    /**
     * 后置处理器
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {

        log.info("--------------netty服务端开始启动--------------");

        boss = new NioEventLoopGroup(1);
        //不指定 默认为CPU核心数的两倍
        worker = new NioEventLoopGroup();

        try{
            //初始化netty 服务端的serverBootstrap对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss,worker);
            serverBootstrap.channel(NioServerSocketChannel.class);

            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    //拿到信道
                    ChannelPipeline pipeline = socketChannel.pipeline();

                    //因为序列化后传递的是数组 , 设置好编码和解码器
                    pipeline.addLast(new ByteArrayDecoder());
                    pipeline.addLast(new ByteArrayEncoder());

                    //添加处理器
                    pipeline.addLast(new RpcServerHandler(handlerMap));
                }
            });

            //是否启用心跳
            serverBootstrap.option(ChannelOption.SO_KEEPALIVE,true);

            //当服务器请求处理线程全满时，用于临时存放已完成三次握手的请求的队列的最大长度
            serverBootstrap.option(ChannelOption.SO_BACKLOG,1024);

            //获取服务的ip:port
            String[] addressArray = serviceAddress.split(":");
            String ip = addressArray[0];
            Integer port = Integer.valueOf(addressArray[1]);

            //启动RPC服务器
            ChannelFuture future = serverBootstrap.bind(ip, port).sync();

            //注册 RPC服务地址
            if(serviceRegistry != null){
                //将该ip:port提供的所有的服务名都注册上去
                for(String interfaceName : handlerMap.keySet()){
                    serviceRegistry.register(interfaceName,serviceAddress);
                    log.info("register service" + interfaceName+"---->"+serviceAddress);
                }
            }
            log.info("RPC server 启动完毕，serviceAddress:"+serviceAddress);

            //监听 关闭RPC服务器
            future.channel().closeFuture().sync();

        }catch (Exception e){
            log.error("RPC服务器启动失败",e);
            e.printStackTrace();
            close();
        }finally {
            close();
        }
    }

    /**
     * 设置上下文
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(XRpcService.class);
        if(!beansWithAnnotation.isEmpty()){
            for(Object serviceBean : beansWithAnnotation.values()){
                XRpcService xRpcService = serviceBean.getClass().getAnnotation(XRpcService.class);
                //获取服务版本号
                String serviceVersion = xRpcService.version();

                //获取服务名
                String serviceName = xRpcService.value().getName();

                //拼接完整服务名
                if(StrUtil.isNotEmpty(serviceVersion)){
                    serviceName = serviceName + "-" + serviceVersion;
                }

                //建立  服务名  ----> 服务对象  关系
                handlerMap.put(serviceName, serviceBean);
            }
        }
    }


    private void close(){
        if(boss!=null){
            boss.shutdownGracefully();
        }
        if(worker!=null){
            worker.shutdownGracefully();
        }
    }
}
